package net.gdface.bean.descriptor;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.google.common.base.Throwables;

import net.gdface.bean.exception.BeanPropertyRuntimeException;
import net.gdface.reflection.FieldUtils;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * 基于反射实现对类的字段{@link Field}读写
 * @author guyadong
 * @since 2.7.0
 */
public class FieldPropertyDescriptor extends BaseNoStandardPropertyDescriptor{

	private final Method readMethod;
	private final Method writeMethod;
	private final Class<?> propertyType;
	private final Field field;
	public FieldPropertyDescriptor(Field field)
			throws IntrospectionException {
		super(checkNotNull(field,"field is null").getName(), null, null);
		this.propertyType = field.getType();
		this.field = field;
		try {
			readMethod = FieldPropertyDescriptor.class.getMethod("readMethod", Object.class,String.class);
			writeMethod = FieldPropertyDescriptor.class.getMethod("writeMethod", Object.class,String.class,Object.class);
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
	private static Field getAccessibleField(Object bean, String name){
		return checkNotNull(FieldUtils.getField(bean.getClass(), name, true),"NOT FIELD DEFINED %s",name);
	}
	/**
	 * 字段读取方法实现
	 * @param bean 读取目标对象
	 * @param name 字段名
	 * @return 字段值
	 */
	public static Object readMethod(Object bean,String name){
		if(null != bean){
			try {
				return getAccessibleField(bean, name).get(bean);
			} catch (Exception e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			}
		}
		return null;		
	}
	/**
	 * 字段写入方法实现
	 * @param bean 写入目标对象
	 * @param name 字段名
	 * @param value 要写入的值
	 */
	public static void writeMethod(Object bean,String name,Object value){
		if(null != bean){
			try{
				getAccessibleField(bean, name).set(bean, value);
			} catch (Exception e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			}
		}
	}

	@Override
	public Method getReadMethod() {
		return readMethod;
	}

	@Override
	public Method getWriteMethod() {
		return writeMethod;
	}

	@Override
	public Class<?> getPropertyType() {
		return propertyType;
	}
	public Field getField() {
		return field;
	}
	public static FieldPropertyDescriptor create(Class<?> beanClass,String name){
		if(null != beanClass && null != name){
			Field field = DescriptorUtils.fieldOf(beanClass, name);
			try {
				return null == field ? null : new FieldPropertyDescriptor(field);
			} catch (IntrospectionException e) {
				throw new BeanPropertyRuntimeException(e);
			}
		}
		return null;
	}
	public static FieldPropertyDescriptor create(Object bean,String name){
		if(null != bean){
			return create(bean.getClass(), name);
		}
		return null;
	}
	@Override
	public Method getReadMethod(Class<?> clazz, PropertyDescriptor descriptor) {
		Field field = ((FieldPropertyDescriptor)descriptor).getField();
		if(!field.getDeclaringClass().isAssignableFrom(clazz)){
            throw new IllegalArgumentException(field.getDeclaringClass().getName() +
                    " is not assignable from " + clazz.getName());
		}
		return descriptor.getReadMethod();
	}
	@Override
	public Method getWriteMethod(Class<?> clazz, PropertyDescriptor descriptor) {
		Field field = ((FieldPropertyDescriptor)descriptor).getField();
		if(!field.getDeclaringClass().isAssignableFrom(clazz)){
            throw new IllegalArgumentException(field.getDeclaringClass().getName() +
                    " is not assignable from " + clazz.getName());
		}
		return descriptor.getWriteMethod();
	}
}
